using System;
using System.Collections.Generic;
using System.Text;
using ALib;
using ALib.Misc;

namespace TradingPlatform
{
    public class TradingSessionString
    {
        private readonly string _tradingSessionString;

        private readonly List<ObjectTuple2<TimeSpan, TimeSpan>> _tradingSessions;

        public TimeSpan DayTradingStartTime
        {
            get
            {
                TimeSpan result = _tradingSessions[0].Object0;
                return result;
            }
        }

        public TimeSpan DayTradingEndTime
        {
            get
            {
                TimeSpan result = _tradingSessions[_tradingSessions.Count - 1].Object1;
                return result;
            }
        }

        public TradingSessionString(string tradingSessionString)
        {
            _tradingSessionString = tradingSessionString;
            _tradingSessions = TradingSessionStringToTradingSessionList(_tradingSessionString);
        }

        public override string ToString()
        {
            return _tradingSessionString;
        }

        public bool InTradingSessionString(TimeSpan time)
        {
            bool result = false;
            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }

        public double TimePercentageLeftInSession(TimeSpan time)
        {
            double result = 0;

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessions)
            {
                if (start_end.Object0 <= time && time <= start_end.Object1)
                {
                    result = 1.0 * (start_end.Object1 - time).Ticks / (start_end.Object1 - start_end.Object0).Ticks;
                    break;
                }
            }
            return result;
        }

        public double TimePercentageLeftInDay(TimeSpan time)
        {
            double result = 0;
            TimeSpan start = DayTradingStartTime;
            TimeSpan end = DayTradingEndTime;
            if (start <= time && time <= end)
            {
                result = 1.0 * (end - time).Ticks / (end - start).Ticks;
            }

            return result;
        }

        public TimeSpan SessionTimeStarted(TimeSpan time)
        {
            TimeSpan result = new TimeSpan(0, 0, 0);

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = time - start_end.Object0;
                    break;
                }
            }
            return result;
        }

        public TimeSpan SessionTimeLeft(TimeSpan time)
        {
            TimeSpan result = new TimeSpan(0, 0, 0);

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = start_end.Object1 - time;
                    break;
                }
            }
            return result;
        }

        public static List<ObjectTuple2<TimeSpan, TimeSpan>> TradingSessionStringToTradingSessionList(string tradingSessionString)
        {
            string[] sessions = tradingSessionString.Split(new char[] { '&' });
            List<ObjectTuple2<TimeSpan, TimeSpan>> tradingSessions = new List<ObjectTuple2<TimeSpan, TimeSpan>>();

            GoodValue.Assert(sessions.Length > 0);

            foreach (string session in sessions)
            {
                string[] startEnd = session.Split(new char[] { '-' });
                GoodValue.Assert(startEnd.Length == 2);

                TimeSpan start = DateTimeConverter.ToTime(startEnd[0]);
                TimeSpan end = DateTimeConverter.ToTime(startEnd[1]);

                tradingSessions.Add(new ObjectTuple2<TimeSpan, TimeSpan>(start, end));
            }
            return tradingSessions;
        }

        public static bool InTradingSession(TimeSpan time, string tradingSessionString)
        {
            TradingSessionString tss = new TradingSessionString(tradingSessionString);
            bool result = tss.InTradingSessionString(time);

            return result;
        }

        private static readonly Dictionary<string, TradingSessionString> _tradingSessionCache = new Dictionary<string, TradingSessionString>();
        public static double TimePercentageLeftInSession(TimeSpan time, string tradingSessionString)
        {
            if (!_tradingSessionCache.ContainsKey(tradingSessionString))
            {
                _tradingSessionCache.Add(tradingSessionString, new TradingSessionString(tradingSessionString));
            }
            double result = _tradingSessionCache[tradingSessionString].TimePercentageLeftInSession(time);

            return result;
        }


        public static TimeSpan SessionTimeStarted(TimeSpan time, string tradingSessionString)
        {
            TimeSpan result = new TimeSpan(0, 0, 0);
            if (!_tradingSessionCache.ContainsKey(tradingSessionString))
            {
                _tradingSessionCache.Add(tradingSessionString, new TradingSessionString(tradingSessionString));
            }

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessionCache[tradingSessionString]._tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = time - start_end.Object0;
                    break;
                }
            }
            return result;
        }

        public static TimeSpan SessionTimeLeft(TimeSpan time, string tradingSessionString)
        {
            TimeSpan result = new TimeSpan(0, 0, 0);
            if (!_tradingSessionCache.ContainsKey(tradingSessionString))
            {
                _tradingSessionCache.Add(tradingSessionString, new TradingSessionString(tradingSessionString));
            }

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessionCache[tradingSessionString]._tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = start_end.Object1 - time;
                    break;
                }
            }
            return result;
        }

        public static TimeSpan[] SessionTimeStartedAndLeft(TimeSpan time, string tradingSessionString)
        {
            TimeSpan[] result = new TimeSpan[] { new TimeSpan(), new TimeSpan() };
            if (!_tradingSessionCache.ContainsKey(tradingSessionString))
            {
                _tradingSessionCache.Add(tradingSessionString, new TradingSessionString(tradingSessionString));
            }

            foreach (ObjectTuple2<TimeSpan, TimeSpan> start_end in _tradingSessionCache[tradingSessionString]._tradingSessions)
            {
                if (start_end.Object0 < time && time <= start_end.Object1)
                {
                    result = new TimeSpan[] { time - start_end.Object0, start_end.Object1 - time };
                    break;
                }
            }
            return result;
        }

        public static double TimePercentageLeftInSession(int tradingPlatformTimeZoneIndex, int exchangeTimeZoneIndex, DateTime tradingPlatformTime, string tradingSessionString)
        {
            double result = 0;

            UtcConverter utcConverter = new UtcConverter(tradingPlatformTimeZoneIndex, exchangeTimeZoneIndex);
            DateTime exchangeTime = utcConverter.TradingPlatformTimeToExchangeTime(tradingPlatformTime);

            result = TimePercentageLeftInSession(exchangeTime.TimeOfDay, tradingSessionString);

            return result;
        }

        public static bool InTradingSession(int tradingPlatformTimeZoneIndex, int exchangeTimeZoneIndex, DateTime tradingPlatformTime, string tradingSessionString)
        {
            double sessionTimePercentageLeft = TimePercentageLeftInSession(tradingPlatformTimeZoneIndex, exchangeTimeZoneIndex, tradingPlatformTime, tradingSessionString);
            bool result = false;
            if (0 < sessionTimePercentageLeft && sessionTimePercentageLeft < 1)
            {
                result = true;
            }
            return result;
        }
    }
}